/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as Handlebars from 'handlebars';
import {
  ClassAnalysis, ConstructorParam, DecoratorConstants, DeserializationMode, GenerationContext, InterfaceAnalysis,
  PropertyAnalysis, PropertyDecorators, PropertyGenerationInfo, PropertyKind
} from '..';
import SerializationPathUtil from '../utils/SerializationPathUtil';
import { CustomError, ErrorCodes } from '../utils/CustomError';

export class HandlebarsTemplateEngine {
  private serializationTemplate?: HandlebarsTemplateDelegate;
  private serializationRegisterTemplate?: HandlebarsTemplateDelegate;
  private readonly registerTemplatePath: string =
    SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerRegisterTemplate.hbs');

  private readonly performanceTemplatePath: string =
    SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerPerformanceTemplate.hbs');
  private readonly strictTemplatePath: string =
    SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerStrictTemplate.hbs');
  private readonly commonTemplatePath: string =
    SerializationPathUtil.pathResolve(__dirname, '../../../template/SerializerTemplate.hbs');

  constructor() {
    this.registerBuiltinHelpers();
    this.registerTemplate();
  }

  render(templateName: string, context: GenerationContext): string {
    const template = this.serializationTemplate;

    if (!template) {
      throw new CustomError(`Template '${templateName}' not found.`, ErrorCodes.TEMPLATE_NOT_FOUND);
    }

    return template({ deserializeTemplate: templateName, ...context });
  }

  renderRegister(context: GenerationContext): string {
    const template = this.serializationRegisterTemplate;

    if (!template) {
      throw new CustomError(`Template not found.`, ErrorCodes.TEMPLATE_NOT_FOUND);
    }

    return template(context);
  }

  registerTemplate(): void {
    try {
      // 性能模式反序列化模板
      const performanceTemplateContent = SerializationPathUtil.readFileSync(this.performanceTemplatePath).toString();
      Handlebars.registerPartial(DeserializationMode.PERFORMANCE, performanceTemplateContent);

      // 严格模式反序列化模板
      const strictTemplateContent = SerializationPathUtil.readFileSync(this.strictTemplatePath).toString();
      Handlebars.registerPartial(DeserializationMode.STRICT, strictTemplateContent);

      const commonTemplateContent = SerializationPathUtil.readFileSync(this.commonTemplatePath).toString();
      this.serializationTemplate = this.loadTemplateFromString(commonTemplateContent);

      const registerTemplateContent = SerializationPathUtil.readFileSync(this.registerTemplatePath).toString();
      this.serializationRegisterTemplate = this.loadTemplateFromString(registerTemplateContent);
    } catch (error) {
      throw new CustomError(`Failed to register template ${error instanceof Error ? error.message : String(error)}`,
        ErrorCodes.TEMPLATE_REGISTER_FAIL);
    }
  }

  registerHelper(name: string, helper: Handlebars.HelperDelegate): void {
    Handlebars.registerHelper(name, helper);
  }

  loadTemplateFromString(template: string): HandlebarsTemplateDelegate {
    return Handlebars.compile(template, { noEscape: true });
  }

  private registerBuiltinHelpers(): void {
    this.registerComparisonHelpers();
    this.registerTypeCheckHelpers();
    this.registerFormattingHelpers();
    this.registerPropertyAnalysisHelpers();
    this.registerConstructorHelpers();
    this.registerLogicHelpers();
  }

  private registerComparisonHelpers(): void {
    this.registerHelper('eq', (arg1: unknown, arg2: unknown) => {
      return (arg1 === arg2);
    });

    this.registerHelper('eqPrimitiveType', (kind: string) => {
      const primitives = ['string', 'number', 'boolean', 'bigint', 'undefined', 'null'];
      return primitives.includes(kind);
    });

    this.registerHelper('eqEmptyType', (kind: string) => {
      return ['null', 'undefined'].includes(kind);
    });
  }

  private registerTypeCheckHelpers(): void {
    this.registerHelper('isNumericPropertyName', (propertyName: string) => {
      return /^\d+$/.test(propertyName);
    });

    this.registerHelper('containsUndefined', (typeText: string) => {
      return typeText.includes('undefined');
    });

    // 条件：All properties are basic type + No undefined + No union type + No @Transient + No @SerialName
    this.registerHelper('canReturnDirectly', (classAnalysis: ClassAnalysis) => {
      // 检查类装饰器是否包含非@Serializable装饰器
      if (this.hasNonSerializableClassDecorator(classAnalysis)) {
        return false;
      }

      // 检查属性装饰器是否包含非@Serializable 装饰器
      if (this.hasNonSerializablePropertyDecorators(classAnalysis)) {
        return false;
      }

      const properties = classAnalysis.properties;

      if (!Array.isArray(properties)) {
        return false;
      }

      return properties.every(prop => this.isPropertyValidForDirectReturn(prop));
    });

    this.registerHelper('isClassType', (cls: ClassAnalysis | InterfaceAnalysis) => {
      return !!(cls as ClassAnalysis).originalClass;
    });
  }

  private hasNonSerializableClassDecorator(classAnalysis: ClassAnalysis): boolean {
    const classDecorators = classAnalysis.originalClass?.getDecorators() || [];
    return classDecorators.some(decorator =>
      decorator.getName() !== 'Serializable'
    );
  }

  private hasNonSerializablePropertyDecorators(classAnalysis: ClassAnalysis): boolean {
    const props = classAnalysis.originalClass?.getProperties();
    if (!props || !Array.isArray(props)) {
      return false;
    }

    for (const prop of props) {
      const decorators = prop.getDecorators();
      if (decorators && Array.isArray(decorators)) {
        const hasNonSerializableDecorator = decorators.some(decorator =>
          !DecoratorConstants.SERIAL_DECORATORS.includes(decorator.getName())
        );
        if (hasNonSerializableDecorator) {
          return true;
        }
      }
    }
    return false;
  }

  private isPropertyValidForDirectReturn(prop: PropertyAnalysis): boolean {
    // 条件4: No @Transient annotation - 如果有 @Transient 属性，不能直接返回
    if (prop.decorators?.isTransient) {
      return false;
    }

    // 条件5: No @SerialName property - 如果有 @SerialName，不能直接返回
    if (prop.decorators?.serialName && prop.decorators.serialName !== prop.name) {
      return false;
    }

    // 条件6: No with property - 如果有 with，不能直接返回
    if (prop.decorators?.with) {
      return false;
    }

    // 条件1: All properties are basic type（null, boolean, number, string, bigint）
    const kind = prop.type?.kind;
    const primitives = ['null', 'boolean', 'number', 'string', 'bigint'];
    if (!primitives.includes(kind)) {
      return false;
    }

    // 条件2: No `undefined` or  `null` type property
    if (kind === 'undefined' || kind === 'null') {
      return false;
    }

    // 条件3: No union type property - 检查是否为联合类型
    if (kind === PropertyKind.UNION) {
      return false;
    }

    return true;
  }

  private registerFormattingHelpers(): void {
    this.registerHelper('formatGenericArgs', (args: string[]) => {
      return Array.isArray(args) && args.length > 0 ? `<${args.join(', ')}>` : '';
    });

    this.registerHelper('formatPropertyName', (property: PropertyAnalysis) => {
      return property.decorators?.serialName || property.name;
    });

    this.registerHelper('toLowerCase', (str: string) => {
      return str.toLowerCase();
    });

    this.registerHelper('genericParametersString', (parameters: string[]) => {
      return parameters.join(', ');
    });

    this.registerHelper('genericParametersESObjString', (parameters: string[]) => {
      return parameters.map(() => 'ESObject').join(', ');
    });

    // 过滤出泛型类型的属性(用于生成静态方法的泛型序列化器参数)
    this.registerHelper('filterGenericProperties', (properties: PropertyGenerationInfo[]) => {
      return properties.filter(p => p.analysis.type.kind === PropertyKind.GENERIC && !p.analysis.decorators?.isTransient);
    });
  }

  private registerPropertyAnalysisHelpers(): void {
    this.registerHelper('getPropertyIndex',
      (property: PropertyGenerationInfo, properties: PropertyGenerationInfo[]) => {
        return properties.findIndex(p => p.analysis.name === property.analysis.name);
      });

    this.registerHelper('postConstructorAssignments',
      (constructorParam?: ConstructorParam[], properties?: PropertyGenerationInfo[]) => {
        return properties?.filter(prop => {
          return !constructorParam?.some((cp) => cp.name === prop.analysis.name);
        });
      });
  }

  private registerConstructorHelpers(): void {
    this.registerHelper('getCtorProperties',
      (constructorParam?: ConstructorParam[], properties?: PropertyGenerationInfo[]) => {
        return constructorParam?.map(param => {
          const prop = properties?.find(p => p.analysis.name === param.name);
          // ✅ 允许构造函数参数包含@Transient属性
          // 如果在properties中找不到，说明该属性被@Transient过滤掉了，跳过
          if (!prop) {
            return null; // 返回null，稍后模板中会过滤掉
          }
          return prop;
        }).filter(p => p !== null); // 过滤掉null值（@Transient属性）
      });
  }

  private registerLogicHelpers(): void {
    // 逻辑与操作
    this.registerHelper('and', (...args: unknown[]) => {
      // args 的最后一个元素是 Handlebars 的 options 对象，需要排除
      const values = args.slice(0, -1);
      return values.every(val => !!val);
    });

    // 逻辑非操作
    this.registerHelper('not', (value: unknown) => {
      return !value;
    });

    // 检查装饰器中是否有 @with
    this.registerHelper('hasDecoratorWith', (decorators?: PropertyDecorators) => {
      return !!(decorators?.with);
    });
  }
}